﻿#region Using directives
using Microsoft.VisualStudio.TestTools.UnitTesting;

using System.Collections.Generic;

using Weazel.Gezel.Model;
using Weazel.Gezel.Model.Errors;
using Weazel.Math;
#endregion

namespace Weazel.Gezel.Parser.Test
{
  [TestClass]
  public class TestParseFsm
  {
    [TestMethod]
    public void TestParseFsm1()
    {
      string fsm =
                  "fsm test_fsm(mydp) {                               \n" +
                  "   state s1, s2, s3, s4, s5;                       \n" +
                  "   initial s0;                                     \n" +
                  "   @s0 (ini, noout) -> s1;                         \n" +
                  "   @s1 if (mul_st_cmd) then (calc, noout) -> s2;   \n" +
                  "                       else (ini, noout) -> s1;    \n" +
                  "   @s2 (calc, noout) -> s3;                        \n" +
                  "   @s3 (calc, noout) -> s4;                        \n" +
                  "   @s4 (calc, noout) -> s5;                        \n" +
                  "   @s5 (ini, omul ) -> s1;                         \n" +
                  "}                                                  \n";

      Tokenizer t = new Tokenizer(fsm);

      Model.Model m = new Model.Model("my_model");

      ParseFsm.Parse(new Set<Token>(Token.Eof), t, m);

      Controller ctrl = m.Controllers["test_fsm"];

      // no errors during parsing ..
      Assert.AreEqual(0, t.Errors.Count);

      // test the validity of the returned controller:
      Assert.AreEqual("test_fsm", ctrl.Name);
      Assert.AreEqual("mydp", ctrl.DatapathRef);

      Assert.AreEqual(6, ctrl.States.Count);

      Assert.AreEqual("s0", ctrl.InitialState.LocalName);
      Assert.IsTrue(ctrl.States.ContainsValue(ctrl.InitialState));

      Assert.IsTrue(ctrl.States.ContainsKey("s0"));
      Assert.IsTrue(ctrl.States.ContainsKey("s1"));
      Assert.IsTrue(ctrl.States.ContainsKey("s2"));
      Assert.IsTrue(ctrl.States.ContainsKey("s3"));
      Assert.IsTrue(ctrl.States.ContainsKey("s4"));
      Assert.IsTrue(ctrl.States.ContainsKey("s5"));

      Assert.AreEqual(6, ctrl.Transitions.Count);

      Assert.AreEqual(TransitionType.SimpleUnconditional, ctrl.Transitions["s0"].TransitionType);
      Assert.AreEqual("ini", ctrl.Transitions["s0"].FirstTargetSfgs[0]);
      Assert.AreEqual("noout", ctrl.Transitions["s0"].FirstTargetSfgs[1]);

      Assert.AreEqual(TransitionType.SimpleConditional, ctrl.Transitions["s1"].TransitionType);
    
      Assert.IsTrue(ctrl.Transitions["s1"].Condition is LeafExpression);

      Assert.AreEqual("calc", ctrl.Transitions["s1"].FirstTargetSfgs[0]);
      Assert.AreEqual("noout", ctrl.Transitions["s1"].FirstTargetSfgs[1]);

      Assert.AreEqual("ini", ctrl.Transitions["s1"].SecondTargetSfgs[0]);
      Assert.AreEqual("noout", ctrl.Transitions["s1"].SecondTargetSfgs[1]);

      // no following if-then-else -> second transtion must be null
      Assert.IsNull(ctrl.Transitions["s1"].SecondTransition);

      Assert.AreEqual(TransitionType.SimpleUnconditional, ctrl.Transitions["s2"].TransitionType);
      Assert.AreEqual("calc", ctrl.Transitions["s2"].FirstTargetSfgs[0]);
      Assert.AreEqual("noout", ctrl.Transitions["s2"].FirstTargetSfgs[1]);

      Assert.AreEqual(TransitionType.SimpleUnconditional, ctrl.Transitions["s3"].TransitionType);
      Assert.AreEqual("calc", ctrl.Transitions["s3"].FirstTargetSfgs[0]);
      Assert.AreEqual("noout", ctrl.Transitions["s3"].FirstTargetSfgs[1]);

      Assert.AreEqual(TransitionType.SimpleUnconditional, ctrl.Transitions["s4"].TransitionType);
      Assert.AreEqual("calc", ctrl.Transitions["s4"].FirstTargetSfgs[0]);
      Assert.AreEqual("noout", ctrl.Transitions["s4"].FirstTargetSfgs[1]);

      Assert.AreEqual(TransitionType.SimpleUnconditional, ctrl.Transitions["s5"].TransitionType);
      Assert.AreEqual("ini", ctrl.Transitions["s5"].FirstTargetSfgs[0]);
      Assert.AreEqual("omul", ctrl.Transitions["s5"].FirstTargetSfgs[1]);
    }

    [TestMethod]
    public void TestParseFsmWithDuplicateTransitions()
    {
      string fsm =
        "fsm ctrl(dummy) {  " +
        "initial s1;        " +
        " state s2;         " +

        // duplicated transition (source state used twice)
        " @s1 	(exec1) -> s2; " +
        " @s1 	(exec1) -> s2; }";

      Tokenizer t = new Tokenizer(fsm);
      Model.Model m = new Model.Model("my_model");
      ParseFsm.Parse(new Set<Token>(Token.Eof), t, m);

      Assert.AreEqual(1, t.Errors.Count);
      Assert.AreEqual(ParseErrorType.DuplicateTranstion, t.Errors[0].ErrorType);
    }

    [TestMethod]
    public void TestEmptySfgList()
    {
      List<ParseError> parseErrors = new List<ParseError>();

      const string description =
          "dp mydp { sfg exec {}}" +
          "fsm ctrl(mydp) { @s1 () -> s2; }";

      Model.Model model = ParseModel.Parse(ref parseErrors, description);

      Assert.AreEqual(1, parseErrors.Count);
      Assert.AreEqual(ParseErrorType.EmptySfgList, parseErrors[0].ErrorType);
    }

    [TestMethod]
    public void TestDuplicateInitialState()
    {
      List<ParseError> parseErrors = new List<ParseError>();

      const string description =
          "dp mydp { sfg exec {}}" +
          "fsm ctrl(mydp) { " +
          " initial s0; " + 
          " initial s1; " +
          " state s2;   " +
          "@s1 (exec) -> s2; }";

      Model.Model model = ParseModel.Parse(ref parseErrors, description);

      Assert.AreEqual(1, parseErrors.Count);
      Assert.AreEqual(ParseErrorType.DuplicateInitialState, parseErrors[0].ErrorType);
    }

    [TestMethod]
    public void TestInitialStateInStateList()
    {
      List<ParseError> parseErrors = new List<ParseError>();

      const string description =
        "dp mydp { sfg exec { } } " +
        "fsm ctrl(mydp) { initial s0; state s0, s1; @s0 (exec) -> s1; } ";

      Model.Model model = ParseModel.Parse(ref parseErrors, description);

      Assert.AreEqual(1, parseErrors.Count);     
      Assert.AreEqual(ParseErrorType.DuplicateStateDefintion,
        parseErrors[0].ErrorType);
    }
  }
}
